package com.novel.disk.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import com.novel.disk.common.FileUtils;
import com.novel.disk.entity.FileEntity;
import com.novel.disk.framework.config.ProjectProperties;
import com.novel.disk.framework.exception.BusinessException;
import com.novel.disk.service.ResourceService;
import com.github.junrar.Junrar;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 默认java实现文件系统
 *
 * @author novel
 * @date 2020/7/13
 */
@Service
public class FileResourceServiceImpl implements ResourceService {
    private String ROOT_PATH = "/";

    public FileResourceServiceImpl(ProjectProperties properties) {
        this.ROOT_PATH = properties.getResource().getRootPath();
    }

    @SneakyThrows
    @Override
    public boolean mkdir(String path) {
        File file = new File(ROOT_PATH, path);
        //如果文件夹不存在
        if (!file.exists()) {
            //创建文件夹
            FileUtils.forceMkdir(file);
        }
        return true;
    }

    @Override
    public List<File> listDir(String path) {
        File file = Paths.get(ROOT_PATH, path).toFile();
        if (file.exists()) {
            File[] files = file.listFiles();
            if (files != null) {
                return Arrays.asList(files);
            }
        }
        return null;
    }

    @SneakyThrows
    @Override
    public boolean upLoad(InputStream inputStream, String targetPath) {
        Path path = Paths.get(ROOT_PATH, targetPath);
        File parent = path.getParent().toFile();
        if (!parent.exists()) {
            FileUtils.forceMkdir(parent);
        }
        Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING);
        return true;
    }

    @SneakyThrows
    @Override
    public boolean remove(String path) {
        File file = Paths.get(ROOT_PATH, path).toFile();
        return remove(file);
    }

    @SneakyThrows
    @Override
    public boolean remove(File file) {
        if (file.exists()) {
            if (file.isDirectory()) {
                FileUtils.deleteDirectory(file);
                return true;
            }
            if (file.isFile()) {
                FileUtils.deleteQuietly(file);
                return true;
            }
        }
        return false;
    }

    @SneakyThrows
    @Override
    public boolean rename(String source, String target) {
        File file = Paths.get(ROOT_PATH, source).toFile();
        return rename(file, target);
    }

    @SneakyThrows
    @Override
    public boolean copyDir(String source, String target) {
        File file = Paths.get(ROOT_PATH, source).toFile();
        if (file.exists()) {
            File newFile = Paths.get(ROOT_PATH, target).toFile();
            if (newFile.exists()) {
                return true;
            }
            if (file.isDirectory()) {
                FileUtils.copyDirectory(file, newFile, true);
                return true;
            }
            if (file.isFile()) {
                FileUtils.copyFile(file, newFile);
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean moveDir(String source, String target) {
        return rename(source, target);
    }

    @SneakyThrows
    @Override
    public boolean moveDir(File file, String target) {
        return rename(file, target);
    }

    @SneakyThrows
    @Override
    public boolean rename(File file, String target) {
        if (file.exists()) {
            File newFile = Paths.get(ROOT_PATH, target).toFile();
            if (newFile.exists()) {
                return true;
            }
            if (file.isDirectory()) {
                FileUtils.moveDirectory(file, newFile);
                return true;
            }
            if (file.isFile()) {
                FileUtils.moveFile(file, newFile);
                return true;
            }
        }
        return false;
    }

    @Override
    public File readBytes(String filePath) {
        return Paths.get(ROOT_PATH, filePath).toFile();
    }

    @SneakyThrows
    @Override
    public byte[] download(FileEntity fileEntity) {
        if (fileEntity.getIsDir()) {
            // 压缩目录下的所有文件，并下载
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ZipOutputStream zip = new ZipOutputStream(outputStream);

            String fileName = fileEntity.getFileName();
            fileName = fileName.replace(fileEntity.getName(), "");
            fileName = Paths.get(fileName).toString();
            if ("/".equals(fileName) || "\\".equals(fileName)) {
                fileName = "";
            }
            zip(fileName, fileEntity, zip);

            IOUtils.closeQuietly(zip);
            return outputStream.toByteArray();
        } else {
            File file = readBytes(fileEntity.getUserId() + fileEntity.getFilePath());
            return FileUtils.getBytesByFile(file);
        }
    }

    @SneakyThrows
    @Override
    public String zip(FileEntity fileEntity) {
        Path path1 = Paths.get(fileEntity.getUserId().toString(), UUID.randomUUID() + ".zip");
        // 压缩目录下的所有文件，并下载
        Path path = Paths.get(ROOT_PATH, path1.toString());
        FileOutputStream fileOutputStream = new FileOutputStream(path.toFile());

        ZipOutputStream zip = new ZipOutputStream(fileOutputStream);

        String fileName = fileEntity.getFileName();
        fileName = fileName.replace(fileEntity.getName(), "");
        fileName = Paths.get(fileName).toString();
        if ("/".equals(fileName) || "\\".equals(fileName)) {
            fileName = "";
        }
        zip(fileName, fileEntity, zip);

        IOUtils.closeQuietly(zip);

        return path1.toString();
    }

    private void zip(String base, FileEntity fileEntity, ZipOutputStream zip) throws IOException {
        //缓冲
        byte[] buffer = new byte[2048];
        // 输入
        FileInputStream fis = null;
        // 条目
        ZipEntry entry = null;
        // 数目
        int count = 0;
        List<FileEntity> entityList = fileEntity.getFileEntityList();
        if (entityList != null && entityList.size() > 0) {
            for (FileEntity entity : entityList) {
                if (entity.getIsDir()) {
                    //递归
                    zip(base, entity, zip);
                } else {
                    String fileName = entity.getFileName();
                    fileName = fileName.replace(base, "");
                    if (fileName.startsWith("/") || fileName.startsWith("\\")) {
                        fileName = fileName.substring(1);
                    }
                    entry = new ZipEntry(Paths.get(fileName).toString());
                    // 加入
                    zip.putNextEntry(entry);
                    fis = new FileInputStream(Paths.get(ROOT_PATH, entity.getUserId().toString(), entity.getFilePath()).toFile());
                    // 读取
                    while ((count = fis.read(buffer, 0, buffer.length)) != -1) {
                        // 写入
                        zip.write(buffer, 0, count);
                    }
                    zip.closeEntry(); // 释放资源
                }
            }
        }
    }

    @SneakyThrows
    @Override
    public File unzip(FileEntity fileEntity) {
        File file = Paths.get(ROOT_PATH, fileEntity.getUserId().toString(), fileEntity.getFilePath()).toFile();
        if ("zip".equalsIgnoreCase(fileEntity.getExtendName())) {
            return ZipUtil.unzip(file, Charset.forName(FileUtils.charset(file)));
        } else if ("rar".equalsIgnoreCase(fileEntity.getExtendName())) {
            File destDir = FileUtil.file(file.getParentFile(), FileUtil.mainName(file));
            try {
                if (!destDir.exists()) {
                    FileUtils.forceMkdir(destDir);
                }
                Junrar.extract(file, destDir);
            } catch (Exception e) {
                remove(destDir);
                throw new BusinessException("文件解压失败,不支持解压该文件！", 500);
            }
            return destDir;
        }
        return null;
    }
}
